package tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import junit.framework.Assert;

import messages.JobRequestResult;
import messages.JobSignUpResult;
import model.Job;
import model.Park;
import model.Role;
import model.User;
import model.WorkLoad;

import org.junit.Before;
import org.junit.Test;

import controller.Controller;
/**
 * 
 * @author James Gunter - jpgunter
 * @author Ben Krack
 * @author Ken Norton
 */
public class ControllerTest {
  
  private List<User> my_users;
  private Controller my_controller;
  
  private User find_me;
  private Park find_park1;
  private Park find_park2;
  private User test_pm;
  
  private String park_data[][] = {
      {"Class Park", "", "District 1", "Northwest"},
      {"Couch Park", "", "District 1", "Northwest"},
      {"Chair Park", "", "District 1", "Northwest"},
      {"Jones Park", "", "District 1", "Northwest"},
      {"Smith Park", "", "District 1", "Northwest"},
      {"Koda Park", "", "District 1", "Northwest"},
      {"Kronk Park", "", "District 1", "Northwest"},
      {"Boring Park", "", "District 1", "Northwest"},
      {"Monkey Park", "", "District 1", "Northwest"},
      {"Public Park", "", "District 1", "Northwest"},
      {"Private Park", "", "District 1", "Northwest"},
      {"Protected Park", "", "District 1", "Northwest"},
      {"Ball Park", "", "District 1", "Northwest"},
      {"Car Park", "", "District 1", "Northwest"},
      {"Mark Park", "", "District 1", "Northwest"},
      {"Field Park", "", "District 1", "Northwest"},
      {"Basket Park", "", "District 1", "Northwest"},
      {"Tennis Park", "", "District 1", "Northwest"},
      {"HotDog Park", "", "District 1", "Northwest"},
      {"Project Park", "", "District 1", "Northwest"},
      {"Urban Park", "", "District 1", "Northwest"},
      {"Blanket Park", "", "District 1", "Northwest"},
      {"Cat Park", "", "District 1", "Northwest"},
      {"Lipton Park", "", "District 1", "Northwest"},
      {"Samsung Park", "", "District 1", "Northwest"},
      {"Nothing Park", "", "District 1", "Northwest"},
      {"Table Park", "", "District 1", "Northwest"},
      {"Floor Park", "", "District 1", "Northwest"},
      {"Done Park", "", "District 1", "Northwest"}
      };
  
  private Calendar cal;
  private JobRequestResult jrr;
  private Job job_SJR;
  private Job job_data[];
  private User pm_SJR;
  private Date week_date_SJR;
  
  /**
   * @author James Gunter
   */
  @Before
  public void setUp(){
    
    my_controller = new Controller();
    
    my_users = new ArrayList<User>();
    my_users.add(new User("John Doe", "jdoe@gmail.com", Role.PARK_MANAGER));
    my_users.add(new User("Jane Doe", "jjdoe@gmail.com", Role.PARK_MANAGER));
    my_users.add(new User("Zark Muckerberg", "zmuck@gmail.com", Role.VOLUNTEER));
    my_users.add(new User("Gill Bates", "gbates@gmail.com", Role.VOLUNTEER));
    my_users.add(new User("Beve Stalmer", "bstal@gmail.com", Role.VOLUNTEER));
    my_users.add(new User("Jeeve Stobs", "jstobs@gmail.com", Role.VOLUNTEER));
    my_users.add(new User("Tinus Lorvalds", "tlor@gmail.com", Role.STAFF_MEMBER));
    my_users.add(new User("Nabe Gewell", "ngew@gmail.com", Role.STAFF_MEMBER));
    
    find_me = new User("find", "find@find.com", Role.PARK_MANAGER);
    my_users.add(find_me);
    my_controller.setUsers(my_users);
    
    test_pm = new User("Park Manager Todd", "pm@pm.net", Role.PARK_MANAGER);
    
    List<Park> parks = new ArrayList<Park>();
    parks.add(new Park("Memorial Park", "", "Seattle", "Northwest", new User("","",Role.PARK_MANAGER)));
    parks.add(new Park("Point Defiance", "", "Tacoma", "Northwest", new User("","",Role.PARK_MANAGER)));
    parks.add(new Park("Heritage Park", "", "Tacoma", "Northwest", new User("","",Role.PARK_MANAGER)));
    parks.add(new Park("Bell Park", "", "Bellevue", "Northwest", new User("","",Role.PARK_MANAGER)));
    parks.add(new Park("Port Park", "", "Portland", "Northwest", new User("","",Role.PARK_MANAGER)));
    parks.add(new Park("Oak Park", "", "Oakland", "Southwest", new User("","",Role.PARK_MANAGER)));
    parks.add(new Park("LA County Park", "", "Los Angeles", "Southwest", new User("","",Role.PARK_MANAGER)));
    parks.add(new Park("NYC Park", "", "New York City", "Northeast", new User("","",Role.PARK_MANAGER)));
    parks.add(new Park("BMore Park", "", "Baltimore", "Northeast", new User("","",Role.PARK_MANAGER)));
    parks.add(new Park("Downtown BMore Park", "", "Baltimore", "Northeast", new User("","",Role.PARK_MANAGER)));
    
    find_park1 = new Park("Gas Works", "", "Tacoma","Northwest",test_pm);
    find_park2 = new Park("Park Place Park", "","Tacoma", "Northwest",test_pm);
    
    
    parks.add(find_park1);
    parks.add(find_park2);
    
    my_controller.setParks(parks);
  }
  
  /**
   * @author Ken Norton
   */
  @Before
  public void setUpSJR() {
    cal = Calendar.getInstance();
    jrr = JobRequestResult.SUCCESS;
    job_SJR = new Job();
    week_date_SJR = new Date();
    pm_SJR = new User("Test1", "Test2", Role.PARK_MANAGER);
    job_SJR.setPark(new Park("Smith Park", "", "Olympia",
        "Northwest", pm_SJR));
    
    job_data = new Job[park_data.length];
    
    for (int i = 0; i < job_data.length; i++) {
      Park park = new Park(park_data[i][0], park_data[i][1], park_data[i][2], park_data[i][3], pm_SJR);
      job_data[i] = new Job();
      job_data[i].setPark(park);
      cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) + 1);
      job_data[i].setDate(cal.getTime());
    }
  }
  
  /**
   * @author James Gunter
   * Tests Controller.getUserByEmail();
   * 
   * Tests the case when the user is found.
   */
  @Test
  public void testGetUserByEmailFound(){
    User found = my_controller.getUserByEmail("find@find.com");
    assertEquals(find_me, found);
  }
  
  
  /**
   * @author James Gunter
   * Tests Controller.getUserByEmail();
   * 
   * Tests the case when the user is not found.
   */
  @Test
  public void testGetUserByEmailNotFound(){
    assertNull(my_controller.getUserByEmail("not_there"));
  }
  
  /**
   * @author James Gunter
   * Tests Controller.getVolunteers()
   * 
   * Tests the case that there are multiple users, but only 4 volunteers. It should return only those four.
   */
  @Test
  public void testGetVolunteers(){
    List<User> expected_volunteers = new ArrayList<User>();
    expected_volunteers.add(new User("Zark Muckerberg", "zmuck@gmail.com", Role.VOLUNTEER));
    expected_volunteers.add(new User("Gill Bates", "gbates@gmail.com", Role.VOLUNTEER));
    expected_volunteers.add(new User("Beve Stalmer", "bstal@gmail.com", Role.VOLUNTEER));
    expected_volunteers.add(new User("Jeeve Stobs", "jstobs@gmail.com", Role.VOLUNTEER));

    
    List<User> actual_volunteers = my_controller.getVolunteers();
    assertEquals(expected_volunteers, actual_volunteers);
    
  }
  
  /**
   * @author James Gunter
   * Tests Controller.getVolunteers()
   * 
   * Tests the case that there are multiple users, but only 4 volunteers. It should return only those four.
   */
  @Test
  public void testGetBlackballed(){
    
    //TODO change to blackballed volunteers
    List<User> expected_volunteers = new ArrayList<User>();
    expected_volunteers.add(new User("Zark Muckerberg", "zmuck@gmail.com", Role.BLACKBALLED));
    expected_volunteers.add(new User("Gill Bates", "gbates@gmail.com", Role.BLACKBALLED));
    
    my_controller.getUserByEmail("zmuck@gmail.com").setUserRole(Role.BLACKBALLED);
    my_controller.getUserByEmail("gbates@gmail.com").setUserRole(Role.BLACKBALLED);

    
    List<User> actual_volunteers = my_controller.getBlackballed();
    assertEquals(expected_volunteers, actual_volunteers);
    
  }
  
  /**
   * @author James Gunter
   * Tests Controller.getDistricts();
   * 
   * Tests the case that there are multiple districts including duplicates and it should
   * return a unique set of districts.
   */
  @Test
  public void testGetDistricts(){
    String[] districts = new String[]{"Seattle", "Tacoma", "Bellevue", "Portland", "Oakland","Los Angeles", "New York City", "Baltimore"};
    
    
    Collection<String> actual_districts = my_controller.getDistricts();
    assertTrue(actual_districts.containsAll(Arrays.asList(districts)));
    assertTrue(Arrays.asList(districts).containsAll(actual_districts));
  }
  
  /**
   * @author James Gunter
   * Tests Controller.getRegions()
   * 
   * Tests the case where there are many parks but only 3 unique Regions.
   */
  @Test
  public void testGetRegions(){
    String[] regions = new String[]{"Northwest", "Southwest", "Northeast"};
    
    Collection<String> actual_regions = my_controller.getRegions();
    assertTrue(actual_regions.containsAll(Arrays.asList(regions)));
    assertTrue(Arrays.asList(regions).containsAll(actual_regions));
  }
  
  /**
   * @author James Gunter
   * Tests Controller.getParkNames()
   * 
   * Tests the case that there are many parks, should return the same number of names.
   */
  @Test
  public void testGetParks(){
    String[] parks = new String[]{"Gas Works", "Point Defiance", "Memorial Park"};
    
    List<Park> test_parks =  new ArrayList<Park>();
    for(String p:parks){
      test_parks.add(new Park(p,"","","",new User("","", Role.PARK_MANAGER)));
    }
    my_controller.setParks(test_parks);
    
    Collection<String> actual_parks = my_controller.getParkNames();
    assertTrue(actual_parks.containsAll(Arrays.asList(parks)));
    assertTrue(Arrays.asList(parks).containsAll(actual_parks));
  }
  
  /**
   * @author James Gunter
   * Tests Controller.getPark();
   * 
   * Tests the case when the park is found.
   */
  @Test
  public void testGetParkFound(){
    assertSame(find_park1, my_controller.getPark("Gas Works"));
  }
  
  /**
   * @author James Gunter
   * Tests Controller.getPark();
   * 
   * Tests the case when the park is not found.
   */
  @Test
  public void testGetParkNotFound(){
    assertNull(my_controller.getPark("Not a Park"));
  }
  
  /**
   * @author James Gunter
   * Tests Controller.getParks()
   * 
   * Tests the case where you are getting the parks as a park manager.
   */
  @Test
  public void testGetParksAsPM(){
    List<Park> actual_parks = my_controller.getParks(test_pm);
    assertEquals(2, actual_parks.size());
    assertTrue(actual_parks.contains(find_park1));
    assertTrue(actual_parks.contains(find_park2));
  }
  
  /**
   * @author James Gunter
   * Tests Controller.getParks()
   * 
   * Tests the case where you are getting the parks as a Volunteer.
   */
  @Test
  public void testGetParksAsVolunteer(){
    List<Park> expected_parks = new ArrayList<Park>();
    expected_parks.add(new Park("Memorial Park", "", "Seattle", "Northwest", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("Point Defiance", "", "Tacoma", "Northwest", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("Heritage Park", "", "Tacoma", "Northwest", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("Bell Park", "", "Bellevue", "Northwest", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("Port Park", "", "Portland", "Northwest", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("Oak Park", "", "Oakland", "Southwest", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("LA County Park", "", "Los Angeles", "Southwest", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("NYC Park", "", "New York City", "Northeast", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("BMore Park", "", "Baltimore", "Northeast", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("Downtown BMore Park", "", "Baltimore", "Northeast", new User("","",Role.PARK_MANAGER))); 
    expected_parks.add(new Park("Gas Works", "", "Tacoma","Northwest",test_pm));
    expected_parks.add(new Park("Park Place Park", "","Tacoma", "Northwest",test_pm));
    
    
    
    List<Park> actual_parks = my_controller.getParks(new User("","", Role.VOLUNTEER));
    assertTrue(actual_parks.containsAll(expected_parks));
    assertTrue(expected_parks.containsAll(actual_parks));
  }
  
  /**
   * @author James Gunter
   * Tests Controller.getParks()
   * 
   * Tests the case where you are getting the parks as a Staff Member.
   */
  @Test
  public void testGetParksAsStaff(){
    List<Park> expected_parks = new ArrayList<Park>();
    expected_parks.add(new Park("Memorial Park", "", "Seattle", "Northwest", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("Point Defiance", "", "Tacoma", "Northwest", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("Heritage Park", "", "Tacoma", "Northwest", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("Bell Park", "", "Bellevue", "Northwest", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("Port Park", "", "Portland", "Northwest", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("Oak Park", "", "Oakland", "Southwest", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("LA County Park", "", "Los Angeles", "Southwest", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("NYC Park", "", "New York City", "Northeast", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("BMore Park", "", "Baltimore", "Northeast", new User("","",Role.PARK_MANAGER)));
    expected_parks.add(new Park("Downtown BMore Park", "", "Baltimore", "Northeast", new User("","",Role.PARK_MANAGER))); 
    expected_parks.add(new Park("Gas Works", "", "Tacoma","Northwest",test_pm));
    expected_parks.add(new Park("Park Place Park", "","Tacoma", "Northwest",test_pm));
    
    
    
    List<Park> actual_parks = my_controller.getParks(new User("","", Role.STAFF_MEMBER));
    assertTrue(actual_parks.containsAll(expected_parks));
    assertTrue(expected_parks.containsAll(actual_parks));
  }
  
  

  /**
   * @author Ben Krack
   * Tests Controller.getPendingJobs()
   * 
   * Tests the case where there are many jobs, but only a subset are pending.
   */
  @Test
  public void testGetPendingJobs() {
    List<Job> expected_pending_jobs = new ArrayList<Job>();
    List<Job> actual_pending_jobs = new ArrayList<Job>();
    Job job1 = new Job();
    Job job2 = new Job();
    job1.setDescription("job 1");
    job2.setDescription("job 2");
    Park park = new Park();
    park.setAddress("hawaii");
    park.setDistrict("downtown");
    park.setName("beach park");
    User manager = new User();
    manager.setUserEmail("fishandpoiboi@poi.com");
    manager.setUserName("Ramon");
    manager.setUserRole(Role.PARK_MANAGER);
    park.setParkManager(manager);
    park.setRegion("Northwest");
    job1.setPark(park);
    job2.setPark(park);
    job1.setDuration(1);
    job2.setDuration(1);
    long millis1 = System.currentTimeMillis();
    long millis2 = System.currentTimeMillis();
    Calendar calendar1 = Calendar.getInstance();
    Calendar calendar2 = Calendar.getInstance();
    calendar1.setTimeInMillis(millis1);
    calendar2.setTimeInMillis(millis2);
    calendar1.add(Calendar.DAY_OF_YEAR, -1);
    calendar2.add(Calendar.DAY_OF_YEAR, 1);
    job1.setDate(calendar1.getTime());
    job2.setDate(calendar2.getTime());
    expected_pending_jobs.add(job2);
    my_controller.submitJobRequest(job1, new User("Zark Muckerberg", "zmuck@gmail.com", Role.PARK_MANAGER));
    my_controller.submitJobRequest(job2, new User("Jeeve Stobs", "jstobs@gmail.com", Role.PARK_MANAGER));
    actual_pending_jobs = my_controller.getPendingJobs();
    assertTrue(expected_pending_jobs.equals(actual_pending_jobs));
  }
  
  /**
   * @author Ken Norton
   */
  @Test
  public void testSignUp() {
    Job job = new Job();
    job.setLightWorkNeeded(5);
    job.setHeavyWorkNeeded(5);
    
    Job job_past = new Job();
    
    SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
    Date date;
    try {
      date = sdf.parse("02/22/2012");
    } catch (ParseException e) {
      date = new Date();
    }
    job_past.setDate(date);
    
    JobSignUpResult jsur = my_controller.signUp(my_users.get(0), job, 
        WorkLoad.LIGHT);
    
    // Test SUCCESS.
    assertEquals("Failed on SUCCESS case.", 
        JobSignUpResult.SUCCESS, jsur);
    
    // Test JOB_IN_PAST.
    jsur = my_controller.signUp(my_users.get(0), job_past, 
        WorkLoad.LIGHT);
    
    assertEquals("Failed on JOB_IN_PAST case.", 
        JobSignUpResult.JOB_IN_PAST, jsur);

    // Test BLACKBALLED.
    my_users.get(1).setUserRole(Role.BLACKBALLED);
    
    jsur = my_controller.signUp(my_users.get(1), job, 
        WorkLoad.LIGHT);
    
    assertEquals("Failed on BLACKBALLED case.", 
        JobSignUpResult.BLACKBALLED, jsur);
    
    // Test OVERCOMMITTED.
    my_controller.getJobs().add(job);
    
    jsur = my_controller.signUp(my_users.get(0), job, 
        WorkLoad.LIGHT);
    
    assertEquals("Failed on OVERCOMMITED case.", 
        JobSignUpResult.OVERCOMMITED, jsur);
    
    
    // Test WORKLOAD_FULL.
    job.setLightWorkFilled(5);
    
    jsur = my_controller.signUp(my_users.get(2), job, 
        WorkLoad.LIGHT);
    
    assertEquals("Failed on WORKLOAD_FULL case.", 
        JobSignUpResult.WORKLOAD_FULL, jsur);
    
    // Test JOB_FULL.
    job.setHeavyWorkFilled(5);
    
    jsur = my_controller.signUp(my_users.get(3), job, 
        WorkLoad.LIGHT);
    
    assertEquals("Failed on JOB_FULL case.", 
        JobSignUpResult.JOB_FULL, jsur);
  }
  
  /**
   * @author Ken Norton
   * Test SUCCESS case.
   */
  @Test
  public void testSubmitJobRequest_SUCCESS() {
    JobRequestResult jrr = 
        my_controller.submitJobRequest(job_SJR, pm_SJR);
    
    assertEquals("Failed on SUCCESS case.", 
        JobRequestResult.SUCCESS, jrr);
  }

  /**
   * @author Ken Norton
   * Test WRONG_PARK_MGR case.
   */
  @Test
  public void testSubmitJobRequest_WPM() {
    jrr = my_controller.submitJobRequest(job_SJR, 
        my_controller.getPark("Port Park").getParkManager());
    
    assertEquals("Failed on WRONG_PARK_MGR case.", 
        JobRequestResult.WRONG_PARK_MGR, jrr);
  }
  
  /**
   * @author Ken Norton
   * Test EXCEEDS_MAX_DURATION case.
   */
  @Test
  public void testSubmitJobRequest_EMD() {
    job_SJR.setDuration(3);
    jrr = my_controller.submitJobRequest(job_SJR, pm_SJR);
    
    assertEquals("Failed on EXCEEDS_MAX_DURATION case.", 
        JobRequestResult.EXCEEDS_MAX_DURATION, jrr);
  }
  
  /**
   * @author Ken Norton
   * Test JOB_IN_PAST case.
   */
  @Test
  public void testSubmitJobRequest_JIP() {
    SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
    Date date;
    try {
      date = sdf.parse("02/22/2012");
    } catch (final ParseException the_exception) {
      date = new Date();
    }
    job_SJR.setDate(date);
    jrr = my_controller.submitJobRequest(job_SJR, pm_SJR);
    
    assertEquals("Failed on JOB_IN_PAST case.", 
        JobRequestResult.JOB_IN_PAST, jrr);
  }
  
  /**
   * @author Ken Norton
   * Test DATE_BEYOND_LIMIT case.
   */
  @Test
  public void testSubmitJobRequest_DBL() {
    Date date;
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.DAY_OF_YEAR, 
        cal.get(Calendar.DAY_OF_YEAR) + 120);
    date = cal.getTime();
    job_SJR.setDate(date);
    job_SJR.setDuration(2);
    
    jrr = my_controller.submitJobRequest(job_SJR, pm_SJR);
    
    assertEquals("Failed on DATE_BEYOND_LIMIT case.", 
        JobRequestResult.DATE_BEYOND_LIMIT, jrr);
  }
  
  /**
   * @author Ken Norton
   * Test MAX_WEEKLY_REACHED case.
   */
  @Test
  public void testSubmitJobRequest_MWR() {
    Date week_date;
    cal = Calendar.getInstance();
    cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) + 10);
    week_date = cal.getTime();
    job_SJR.setDate(week_date);
    
    for (int i = 0; i <= 7; i++) {
      my_controller.submitJobRequest(job_SJR, pm_SJR);
    }
    
    jrr = my_controller.submitJobRequest(job_SJR, pm_SJR);
    assertEquals("Failed on MAX_WEEKLY_REACHED case.", 
        JobRequestResult.MAX_WEEKLY_REACHED, jrr);
  }
  
  /**
   * @author Ken Norton
   * Test MAX_JOBS_REACHED case.
   */
  @Test
  public void testSubmitJobRequest_MJR() {
    cal = Calendar.getInstance();
    for (int i = 0; i < job_data.length; i++) {
      jrr = my_controller.submitJobRequest(job_data[i], pm_SJR);
    }
    
    jrr = my_controller.submitJobRequest(job_SJR, pm_SJR);
    assertEquals("Failed on MAX_JOBS_REACHED case.", 
        JobRequestResult.MAX_JOBS_REACHED, jrr);
    
  }
}
